#include "../include/programs.h"
#include "../include/kc.h"
#include "../include/process.h"
#include "../include/syscalls.h"
#include "../include/fileSystem/dirent.h"
#include "../include/fileSystem/file.h"

int
fortune(int argc, char ** argv){
	printstring("Te vas a sacar un 10 en el TPE2 de SO \n");
	return 1;
}

int
illegalAccessor(int argc, char ** argv) {
	char str;
	if (argc == 0)
		*((&str) + 100) = 'h';
	if (argc > 0)
		*((&str) - atoi(argv[0])) = 'h';

	return 0;
}

int
cpuConsumer(int argc, char ** argv) {
	while (1) {
		//solo se sale si matan al proceso
	}
	return 1;
}

int
cpuYielder(int argc, char ** argv) {
	while (1) {
		//solo se sale si matan al proceso
		int k = 10;
		//con esto puedo ver en el top como voy mejorando de prioridad
		while (k--)
			yield();
		sleep(1);
	}
	return 1;
}

int
timer(int argc, char ** argv){
	if (argc != 1 && argc != 2) {
		printstring("Modo de uso: timer cantidadSegundos [nombreArchivo]\n");
		return 0;
	}
	int timer = atoi(argv[0]);
	if (timer <= 0 || timer > 500) {
		printstring("El numero debe estar entre 1 y 500\n");
		return 0;
	}

	//escribo a salida estandar
	if (argc == 1) {
		do {
			printf("%d; ", timer--);
			sleep(1);
		}while(timer > 0);
		putchar('\n');
	}
	else {	//escribo a un archivo
		int fd = open(argv[1], O_CREAT | O_WRITE);
		if (fd == -1) {
			printstring("No se pudo abrir el archivo\n");
			return 0;
		}
		char buff[4], aux;
		do {
			itoa(timer--, buff, 4);
			aux = strlen(buff);
			*(buff + aux) = ';';
			write(fd, buff, aux + 1, 0);
			sleep(1);
		}while(timer > 0);
		close(fd);
	}

	return 1;
}

int
char_lifter(int argc, char ** argv){
	printstring("El caracter ingresado es: \n");
	putchar(argv[0][0]);
	putchar('\n');
	return 1;
}

int
frase(int argc, char ** argv){
	printstring("Tu ");
	printstring(argv[0]);
	printstring(" es ");
	printstring(argv[1]);
	printstring(".\n");
	return 1;
}

int
fill(int argc, char ** argv){
	if (argc != 3) {
		printstring("Modo de uso: fill nombreArchivo caracter cantidad\n");
		return 0;
	}

	int cant = atoi(argv[2]);
	int fd = open(argv[0], O_CREAT | O_WRITE);

	if (fd == -1) {
		printstring("No se pudo abrir el archivo\n");
		return 1;
	}

	char buffer[6000], c = *(argv[1]);
	int count = (cant <= 6000)? cant : 6000;

	int i;
	for (i = 0; i < count; i++) {
		buffer[i] = c;
	}

	if (count == 0) {
		close(fd);
		return 1;
	}
	if (write(fd, buffer, cant % count, 0) < 0) {
		printstring("Error al tratar de llenar el archivo\n");
		close(fd);
		return 1;
	}
	cant -= (cant % count);

	while (cant > 0) {
		if (write(fd, buffer, count, 0) < 0) {
			printstring("Error al tratar de llenar el archivo\n");
			break;
		}
		cant -= count;
	}

	close(fd);

	return 1;
}


int
help(int argc, char ** argv){
	printstring("Help para PontiOS v 1.31\n\n"
	"Comandos disponibles:\n"
	"fortune - Predice el futuro.\n"
	"charLifter C - Levanta un caracter C de la entrada y lo devuelve.\n"
	"frase S A - Recibe dos parametros, un sustantivo S y un adjetivo A, y "
	"forma\n una frase a partir de ellos.\n"
	"sampleText - Muestra un texto de prueba para probar el copiado "
	"del mouse.\n"
	"sampleCommands - Muestra un listado de comandos para probar que "
	"con el copiado.\n "
	"y pegado del mouse los comandos se ejecutan correctamente\n"
	"clear - Limpia la pantalla.\n"
	"top - Muestra el estado actual de los procesos que se estan "
	"ejecutando\n en el sistema.\n"
	"illegalAccesor - trata de acceder a una zona no permitida de "
	"la memoria.\n"
	"timer N [A] - Realiza una cuenta regresiva a partir de N. Imprime los numeros\n"
	" a la salida estandar, o al archivo A.\n"
	"write - Crea un archivo con el nombre del primer parametro y el contenido del\n"
	" segundo, en el directorio actual.\n"
	"cd - Cambia el directorio actual, recibe un path.\n"
	"cpuConsumer - Consume CPU haciendo un ciclo infinito.\n"
	"cpuYielder - Cede el CPU dentro de un ciclo infinito.\n"
	"kill - Mata el proceso con el numero de Pid que se le pase.\n"
	"mkdir - Crea un directorio con el nombre que se le pase.\n"
	"rename - Renombra un archivo o directorio. Recibe el nombre del archivo a\n"
	" modificar, y su nuevo nombre.\n"
	"rmdir - Elimina un directorio. No puede estar vacio.\n"
	"rm - Elimina un archivo.\n"
	"ls - Lista los archivos y directorios en el directorio actual, o en que se le\n"
	" pase por parametro.\n"
	"find - Busca archivos o directorios a partir del directorio actual.\n"
	"cat - Imprime el contenido del archivo que se le pase por parametro en la\n"
	" salida estandar.\n"
	"fill ARCH C N - Llena el archivo ARCH con N caracteres C.\n"
	"help - Muestra esta ayuda.\n\n"
	"Opciones de la shell:\n"
	"& - Ejecuta un programa en background.\n"
	"-pN - Ejecuta un programa con prioridad N, con N entre 0 y 4.\n\n");

	return 1;
}

int
sample_text(int argc, char ** argv){
printstring(
"In a hole in the ground there lived a hobbit. Not a nasty, dirty, wet hole, \n"
"filled with the ends of worms and an oozy smell, nor yet a dry, bare, sandy\n"
"hole with nothing in it to sit down on or to eat: it was a hobbit-hole, and\n"
"that means comfort.\n"
"It had a perfectly round door like a porthole, painted green, with a shiny \n"
"yellow brass knob in the exact middle. The door opened on to a tube-shaped "
"hall\n"
"like a tunnel: a very comfortable tunnel without smoke, with panelled "
"walls, \n"
"and floors tiled and carpeted, provided with polished chairs, and lots and "
"lots\n"
"of pegs for hats and coats - the hobbit was fond of visitors. The tunnel "
"wound\n"
"on and on, going fairly but not quite straight into the side of the hill \n"
"- The Hill, as all the people for many miles round called it - and many "
"little\n"
"round doors opened out of it, first on one side and then on another. No "
"going\n"
"upstairs for the hobbit: bedrooms, bathrooms, cellars, pantries (lots of \n"
"these), wardrobes (he had whole rooms devoted to clothes), kitchens, \n"
"dining-rooms, all were on the same floor, and indeed on the same passage. "
"The \n"
"best rooms were all on the left-hand side (going in), for these were the "
"only \n"
"ones to have windows, deep-set round windows looking over his garden and \n"
"meadows beyond, sloping down to the river.\n");

	return 1;
}

int
sample_commands(int argc, char ** argv){
	printstring("frase perro lindo\n"
			"frase casa grande\n"
			"fortune\n"
			"comandoErroneo\n"
			"help\n"
			"frase instinto agudo\n"
			"fortune\n"
			"sampleText\n"
			"ls\n"
			"charLifter R\n");
	return 1;
}

int
top(int argc, char ** argv){

	int cantProc = 0, prevCantProc = 0, i;
	process_info_t procs[23];
	char * state[4] = {"BLOCKED", "READY", "RUNNING", "DEAD"};

	clear();
	//escondo el cursor de video para que el top se vea mejor
	hideCursor();

	printstring("PID    NAME           TTY    PRIORITY IS_BACK STATUS  %CPU");

	char exitFlag = 0;
	while(!exitFlag) {
		//guardo en prevCantProc la cantidad de procesos de la pasada previa
		prevCantProc = cantProc;

		//buscar los procesos e imprimirlos
		cantProc = getProcs(procs, 23);

		for(i=0; i < cantProc; i++){
			setVideoXY(0, i+1);
			printf("%-6d %-14s %-6d %-8d %-7s %-7s %-3d", procs[i].pid, procs[i].name,
				procs[i].tty + 1, procs[i].priority,
				procs[i].background == 1 ? "yes":"no", state[procs[i].state],
				procs[i].cpuUsage);
		}

		//si prevCantProc es mayor a cantProc, voy a entrar aca y limpiar las
		//lineas de la tabla con procesos viejos
		for(; i < prevCantProc; i++){
			setVideoXY(0, i+1);
			printstring("                                                          ");
		}

		sleep(2);

		char c;
		while ((c = getcharNoBlock()) != 0 && !exitFlag) {
			if (toupper(c) == 'Q')
				exitFlag = 1;
		}
	}
	putchar('\n');

	showCursor();

	return 1;
}

int
ls(int argc, char ** argv) {
	int fd;
	char * dirPath;

	if (argc == 1)
		dirPath = argv[0];
	else
		dirPath = ".";

	if ( (fd = opendir(dirPath)) == -1) {
		printstring("No se pudo listar el directorio\n");
		return 0;
	}

	chdir(dirPath);

	dirEntry dirEnt;
	while (readdir(fd, &dirEnt) != -1) {
		printf("%c %-4d ", dirEnt.type, getFileSize(dirEnt.name));
		//si es directorio, lo imprimo en otro color
		if (dirEnt.type == 'd') {
			videoAttr(0x01);	//letras azules
			printstring(dirEnt.name);
			videoAttr(0x07);	//letras blancas
		}
		else {
			printstring(dirEnt.name);
		}
		putchar('\n');
	}
	closedir(fd);

	return 1;
}

#define FIND_PATH_LEN 200

static int
findFileRecur(char * path, char * name, int size, int idx) {
	int fd;
	char foundFlag = 0;
	//si devuelve -1 lo mas probable es que trate de abrir algo que no era
	//un archivo, asi que salgo
	if ( (fd = opendir(path)) == -1) {
		return 0;
	}

	dirEntry dirEnt;
	//itero en el directorio
	while (readdir(fd, &dirEnt) != -1) {
		//ignoro las entradas de '.' y '..'
		if (strcmp(dirEnt.name, ".") != 0 && strcmp(dirEnt.name, "..") != 0) {
			int len = strlen(dirEnt.name) + 1; //+1 por el '/' que agrego

			//me quede sin espacio, salgo
			if (idx + len >= size) {
				closedir(fd);
				return -1;
			}

			//me guardo el nombre del archivo o directorio que estoy viendo,
			//y asi voy armando el path hacia el archivo que voy a devolver
			path[idx] = '/';
			strcpy(&path[idx+1], dirEnt.name);

			//si era el archivo, lo imprimo y sigo buscando mas apariciones
			if (strcmp(dirEnt.name, name) == 0) {
				if (dirEnt.type == 'd')
					videoAttr(0x01);	//letras azules
				printstring(path + 2);
				if (dirEnt.type == 'd')
					videoAttr(0x07);	//letras blancas
				putchar('\n');
				foundFlag = 1;
			}
			//me meto recursivamente para buscar apariciones del archivo
			foundFlag |= findFileRecur(path, name, size, idx + len);
		}
	}
	closedir(fd);
	return foundFlag;
}

int
findFile(int argc, char ** argv) {
	if (argc != 1) {
		printstring("Modo de uso: find nombreDeArchivo\n");
		return 1;
	}

	char path[FIND_PATH_LEN];
	strcpy(path, ".");

	if (findFileRecur(path, argv[0], FIND_PATH_LEN, 1) == 0)
		printstring("No se encontro el archivo\n");

	return 1;
}

int
cat(int argc, char ** argv) {
	if (argc != 1) {
		printstring("Modo de uso: cat nombreDeArchivo\n");
		return 1;
	}

	int fd = open(argv[0], 0);

	if (fd == -1) {
		printstring("No se encontro el archivo\n");
		return 1;
	}

	int cant;
	char buf[21], flag = 0;

	//indico con flag que al menos algo lei
	if ((cant = read(fd, buf, 20, 0)) > 0)
		flag = 1;

	//imprimo y leo mientras haya algo para leer
	do {
		buf[cant] = '\0';
		printstring(buf);
	}while ( (cant = read(fd, buf, 20, 0)) > 0);

	if (flag)
		putchar('\n');

	close(fd);

	return 1;
}

int
writer(int argc, char ** argv) {
	if (argc != 2) {
		printstring("Modo de uso: write nombreDeArchivo \"textoParaEscribir\"\n");
		return 1;
	}

	int fd = open(argv[0], O_CREAT | O_WRITE);

	if (fd == -1) {
		printstring("No se pudo abrir el archivo\n");
		return 1;
	}

	int cant = strlen(argv[1]);
	if (write(fd, argv[1], cant, 0) < cant)
		printstring("No se pudo escribir todo el archivo\n");

	close(fd);

	return 1;
}
